/*
*  This file is part of mangusta
*
*  mangusta is free software; you can redistribute it and/or modify
*  it under the terms of the GNU General Public License as published by
*  the Free Software Foundation; either version 2 of the License, or
*  (at your option) any later version.
*
*  mangusta is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU General Public License for more details.
*
*  You should have received a copy of the GNU General Public License
*  along with mangusta; if not, write to the Free Software
*  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  US
*
*  Author: <antonino.calderone@ericsson.com>, <acaldmail@gmail.com>
*
*/


/* -------------------------------------------------------------------------- */

#include "nu_variant.h"
#include "nu_functions.h"
#include "nu_eval_expr.h"
#include "nu_builtin_help.h"
#include "nu_lang_defs.h"

#include <functional>


/* -------------------------------------------------------------------------- */

namespace nu
{

/* -------------------------------------------------------------------------- */

#define THIS_MODULE "math"
#define MD_NAMESPACE THIS_MODULE "."

#define _INT PROGLANG_TYPE_INTEGER
#define _STR PROGLANG_TYPE_STRING
#define _DBL PROGLANG_TYPE_DOUBLE
#define _FLT PROGLANG_TYPE_FLOAT

/* -------------------------------------------------------------------------- */

static struct module_math_t : public module_t
{
   module_math_t()
   {
      add_module(this, THIS_MODULE);
   }

   virtual bool load() throw() override
   {
      auto & hmap = builtin_help_t::get_instance();

      hmap[ {lang_item_t::MODULE, THIS_MODULE}] =
      {
         "The " THIS_MODULE " module provides access to the mathematical functions",
         THIS_MODULE" module"
      };

      functions_t & fmap = functions_t::get_instance();

      /*
      __FUNCTOR_BUILDER(sin)

      ===>

      struct _sin { float operator()(float x) throw() { return ::sin(x); } };
      fmap["sin"] = functor<float, _sin>;
      */


#ifdef __FUNCTOR_BUILDER
#warning "__FUNCTOR_BUILDER already defined..."
#undef __FUNCTOR_BUILDER
#endif


#define __FUNCTOR_BUILDER(_FNC_) \
   struct _##_FNC_{ float operator()(float x) throw() { return _FNC_(x); } }; \
   fmap[MD_NAMESPACE #_FNC_] = math_functor<float, _##_FNC_>;

      __FUNCTOR_BUILDER(truncf);


      hmap[ {lang_item_t::FUNCTION, MD_NAMESPACE "truncf"}] =
      {
         "Rounds x toward zero, returning the nearest integral value"
         " that is not larger in magnitude than x",
         _FLT" " MD_NAMESPACE "truncf("_FLT" x)"
      };

#ifdef __DOUBLE_FUNCTOR_BUILDER
#warning "__FUNCTOR_BUILDER already defined..."
#undef __DOUBLE_FUNCTOR_BUILDER
#endif


#define __DOUBLE_FUNCTOR_BUILDER(_FNC_) \
   struct _##_FNC_{ double operator()(double x) throw() { return _FNC_(x); } }; \
   fmap[MD_NAMESPACE #_FNC_] = math_functor<double, _##_FNC_>;

      __DOUBLE_FUNCTOR_BUILDER(sin);
      __DOUBLE_FUNCTOR_BUILDER(cos);
      __DOUBLE_FUNCTOR_BUILDER(tan);
      __DOUBLE_FUNCTOR_BUILDER(log);
      __DOUBLE_FUNCTOR_BUILDER(log10);
      __DOUBLE_FUNCTOR_BUILDER(exp);
      __DOUBLE_FUNCTOR_BUILDER(abs);
      __DOUBLE_FUNCTOR_BUILDER(asin);
      __DOUBLE_FUNCTOR_BUILDER(acos);
      __DOUBLE_FUNCTOR_BUILDER(atan);
      __DOUBLE_FUNCTOR_BUILDER(sinh);
      __DOUBLE_FUNCTOR_BUILDER(cosh);
      __DOUBLE_FUNCTOR_BUILDER(tanh);
      __DOUBLE_FUNCTOR_BUILDER(sqrt);

      hmap[ {lang_item_t::FUNCTION, MD_NAMESPACE "sinh"}] =
      {
         "Returns the hyperbolic sine of x radians",
         _DBL " " MD_NAMESPACE "sinh("_DBL" x)"
      };

      hmap[ {lang_item_t::FUNCTION, MD_NAMESPACE "tanh"}] =
      {
         "Returns the hyperbolic tangent of x radians",
         _DBL " " MD_NAMESPACE "tanh("_DBL" x)"
      };

      hmap[ {lang_item_t::FUNCTION, MD_NAMESPACE "cosh"}] =
      {
         "Returns the hyperbolic cosine of x radians",
         _DBL " " MD_NAMESPACE "cosh("_DBL" x)"
      };

      hmap[ {lang_item_t::FUNCTION, MD_NAMESPACE "asin"}] =
      {
         "Returns the principal value of the arc sine of x, "
         "expressed in radians",
         _DBL " " MD_NAMESPACE "asin("_DBL" x)"
      };

      hmap[ {lang_item_t::FUNCTION, MD_NAMESPACE "acos"}] =
      {
         "Returns the principal value of the arc cosine of x, "
         "expressed in radians",
         _DBL " " MD_NAMESPACE "acos("_DBL" x)"
      };

      hmap[ {lang_item_t::FUNCTION, MD_NAMESPACE "atan"}] =
      {
         "Returns the principal value of the arc tangent of x, "
         "expressed in radians",
         _DBL " " MD_NAMESPACE "atan("_DBL" x)"
      };

      hmap[ {lang_item_t::FUNCTION, MD_NAMESPACE "sin"}] =
      {
         "Returns the trigonometric sine of x radians",
         _DBL " " MD_NAMESPACE "sin("_DBL" x)"
      };

      hmap[ {lang_item_t::FUNCTION, MD_NAMESPACE "cos"}] =
      {
         "Returns the trigonometric cosine of x radians",
         _DBL " " MD_NAMESPACE "cos("_DBL" x)"
      };

      hmap[ {lang_item_t::FUNCTION, MD_NAMESPACE "tan"}] =
      {
         "Returns the trigonometric tangent of x, in radians",
         _DBL " " MD_NAMESPACE "tan("_DBL" x)"
      };


      hmap[ {lang_item_t::FUNCTION, MD_NAMESPACE "log"}] =
      {
         "Returns the natural logarithm of x",
         _DBL " " MD_NAMESPACE "log("_DBL" x)"
      };

      hmap[ {lang_item_t::FUNCTION, MD_NAMESPACE "log10"}] =
      {
         "Returns common (base-10) logarithm of x",
         _DBL " " MD_NAMESPACE "log10("_DBL" x)\n"
      };

      hmap[ {lang_item_t::FUNCTION, MD_NAMESPACE "exp"}] =
      {
         "Returns e (the base of natural logarithms) to the power of x",
         _DBL " " MD_NAMESPACE "exp("_DBL" x)"
      };

      hmap[ {lang_item_t::FUNCTION, MD_NAMESPACE "abs"}] =
      {
         "Returns the absolute value of the expression x",
         _DBL " " MD_NAMESPACE "abs("_DBL" x)"
      };


      hmap[ {lang_item_t::FUNCTION, MD_NAMESPACE "sqrt"}] =
      {
         "Returns the square root of x",
         _DBL " " MD_NAMESPACE "sqrt("_DBL" x)"
      };


      struct _sign
      {
         double operator()(double x) throw()
         {
            return x > 0.0F ? 1.0F : (x == 0.0F ? 0.0F : -1.0F);
         }
      };


      fmap[MD_NAMESPACE "sign"] = math_functor < double, _sign > ;

      hmap[ {lang_item_t::FUNCTION, MD_NAMESPACE "sign"}] =
      {
         "Returns the sign of x. If x=0 returns 0, "
         "if x<0 retunrs -1 else 1",
         _DBL " " MD_NAMESPACE "sign("_DBL" x)"
      };


      struct _min
      {
         double operator()(double x, double y) throw()
         {
            return x < y ? x : y;
         }
      };


      fmap[MD_NAMESPACE "min"] = math_functor2 < double, _min > ;

      hmap[ {lang_item_t::FUNCTION, MD_NAMESPACE "min"}] =
      {
         "Returns the smallest of x and y",
         _DBL " " MD_NAMESPACE "min("_DBL" x, "_DBL" y)"
      };


      struct _max
      {
         double operator()(double x, double y) throw()
         {
            return x > y ? x : y;
         }
      };

      fmap[MD_NAMESPACE "max"] = math_functor2 < double, _max > ;

      hmap[ {lang_item_t::FUNCTION, MD_NAMESPACE "max"}] =
      {
         "Returns the largest of x and y",
         _DBL " " MD_NAMESPACE "max("_DBL" x, "_DBL" y)"
      };


      struct _pow
      {
         double operator()(double x, double y) throw()
         {
            return ::pow(x, y);
         }
      };

      fmap[MD_NAMESPACE "pow"] = math_functor2 < double, _pow > ;

      hmap[ {lang_item_t::FUNCTION, MD_NAMESPACE "pow"}] =
      {
         "Returns base x raised to the power exponent y",
         _DBL " " MD_NAMESPACE "pow("_DBL" x, "_DBL" y)"
      };


      struct _int_truncate
      {
         int operator()(double x) throw()
         {
            // Truncate to greatest integer less or equal to Argument
            // Example:
            // trunc(-5) => -5
            // trunc(-5.1) => -6
            // trunc(5.9) => 5
            return int((x < 0.0F) ?
                       int((abs(x)) - int(abs(x))>0.0F ?
                           int(x) - 1 : int(x)) : int(x));
         }
      };

      // Truncate to greatest integer less or equal to Argument
      fmap[MD_NAMESPACE "trunc"] = functor_int_double < _int_truncate > ;

      hmap[ {lang_item_t::FUNCTION, MD_NAMESPACE "trunc"}] =
      {
         "Truncates an expression to a whole number",
         _INT " " MD_NAMESPACE "trunc("_DBL" x)"
      };

      auto functor_rnd = [](
                            rt_prog_ctx_t & ctx,
                            const std::string& name,
                            const nu::func_args_t & args)
      {
         return double(::rand()) / RAND_MAX;
      };


      fmap[MD_NAMESPACE "rnd"] = functor_rnd;

      hmap[ {lang_item_t::FUNCTION, MD_NAMESPACE "rnd"}] =
      {
         "Returns a random number between 0 and 1",
         _DBL " " MD_NAMESPACE "rnd()"
      };

      struct _bitwise_not
      {
         int operator()(int x) throw()
         {
            return ~x;
         }
      };

      fmap[MD_NAMESPACE "bnot"] = math_functor < int, _bitwise_not > ;

      hmap[ {lang_item_t::FUNCTION, MD_NAMESPACE "bnot"}] =
      {
         "Inverts all of the bits of the operand",
         _INT " " MD_NAMESPACE "bnot("_INT" x)"
      };

      auto functor_pi = [](
                           rt_prog_ctx_t & ctx,
                           const std::string& name,
                           const nu::func_args_t & args)
      {
         functions_t::check_arg_num(args, 0, name);
         return nu::variant_t(3.1415926535897F);
      };

      fmap[MD_NAMESPACE "pi"] = functor_pi;

      hmap[ {lang_item_t::FUNCTION, MD_NAMESPACE "pi"}] =
      {
         "Returns the ratio of a circle's circumference "
         "to its diameter, approximately equal to 3.14159",
         _DBL " " MD_NAMESPACE "pi()"
      };

      return true;
   }


   /* -------------------------------------------------------------------------- */

}
module_instance;


/* -------------------------------------------------------------------------- */

} // nu

